package com.db.priceApp.service;

import java.io.IOException;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.util.Set;
import java.util.logging.Logger;

import com.db.priceApp.domain.InstrumentList;
import com.db.priceApp.domain.Price;
import com.db.priceApp.domain.clientDataBasa.ClientProfile;
import com.db.priceApp.domain.clientDataBasa.ClientsBasa;
import com.db.priceApp.network.protocol.MessageTranciever;

public class PriceSender implements Runnable {

	private static final long TIME_STEP = 5000;
	private static final int AVERAGE_PROCESS_TIME = 10;
	private final InstrumentList instrList;
	private MessageTranciever mt;
	private final ClientsBasa clientBasa;
	private Logger logger = Logger.getLogger(PriceSender.class.getName());
	private final int numOfThreads;

	public PriceSender(final InstrumentList list, final ClientsBasa basa,
			int sizeOfThreadPool) {

		instrList = list;
		clientBasa = basa;
		numOfThreads = sizeOfThreadPool;
		mt = new MessageTranciever();
	}

	@Override
	public void run() {
		ClientProfile client;
		long time;
		int numOfClients;
		while (true) {
			try {
				
				client = clientBasa.getNextSubscriber();
				if (client == null) {
					continue;
				}
				if (!clientBasa.containsClient(client)) {
					continue;
				}
				process(client);
				
				clientBasa.putSubscriber(client);
				
				numOfClients = clientBasa.getNumOfClients();
				time = TIME_STEP - numOfClients / numOfThreads * AVERAGE_PROCESS_TIME;
				Thread.sleep(time);
				
			} catch (IOException e) {
				logger.severe("server failure: " + e.getMessage());
			} catch (InterruptedException e) {
				logger.warning("interruption");
			}
		}
	}

	private void process(ClientProfile client) throws IOException {
		Price price;
		Set<String> subscriptions = client.getSubscribtions();
		byte[] marketDataResponse;
		SelectionKey key = client.getSelectionKey();

		if (!isProperTime(client)) {
			return;
		}

		for (String instr : subscriptions) {
			try {
				price = instrList.getInstrumentPrice(instr);
				marketDataResponse = MessageCostructor
						.createPriceReportMessage(instr, price,
								System.currentTimeMillis());

				mt.transmit(marketDataResponse, (SocketChannel) key.channel());

			} catch (IllegalArgumentException e) {
				logger.warning("no such instrument " + instr);
				marketDataResponse = MessageCostructor
						.createRejectMessage("no such instrument " + instr);

				mt.transmit(marketDataResponse, (SocketChannel) key.channel());

			}
		}

	}

	private boolean isProperTime(ClientProfile client) {
		if ((System.currentTimeMillis() - client.getLastTransmitionTime()) < TIME_STEP) {
			return false;
		}

		client.setLastTransmitionTime(System.currentTimeMillis());
		return true;
	}

}
